package user.zc.excel.data.builder;

import user.zc.excel.WriteDTO;
import user.zc.excel.data.info.DataCell;
import user.zc.excel.data.info.ICell;
import user.zc.excel.data.style.IStyle;
import lombok.SneakyThrows;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class DataBuilder<T> implements IBuilder<T, List<ICell.CellInfo[]>>{
    private WriteDTO<T> writeDTO;
    public DataBuilder(WriteDTO writeDTO){
        this.writeDTO = writeDTO;
    }
    @Override
    @SneakyThrows
    public List<ICell.CellInfo[]> collect(Class<T> clazz) {
        List<Field> fields = fields(clazz);
        List<ICell.CellInfo[]> list = new ArrayList<>();
        for (T data : writeDTO.getDatas()) {
            ICell.CellInfo[] cellInfos = new ICell.CellInfo[fields.size()];
            beforeUseInvokeUpdate(data);
            for (int i = 0; i < fields.size(); i++) {
                Field field = fields.get(i);
                cellInfos[i] = new DataCell(data,writeDTO.getStyles().get(field).clone().extend(beforeUseInvokeStyle(i,data,field))).parse(field);
            }
            list.add(cellInfos);
        }
        return list;
    }
    @SneakyThrows
    public T beforeUseInvokeUpdate(T item){
        Method method = deepGetMethod(item.getClass(),"update");
        if(method!=null){
            method.setAccessible(true);
            method.invoke(item);
        }
        return item;
    }
    @SneakyThrows
    public IStyle.StyleInfo beforeUseInvokeStyle(Integer i,T item, Field field ){
        Method method = deepGetMethod(item.getClass(),"style",Integer.class,Object.class,String.class,Object.class);
        if(method!=null){
            method.setAccessible(true);
            field.setAccessible(true);
            return (IStyle.StyleInfo)method.invoke(item,i,item,field.getName(),field.get(item));
        }else{
            return IStyle.StyleInfo.newEmpty();
        }
    }

    private Method deepGetMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
        Method method =  null;
        for(Class clz = clazz; clz != Object.class; clz = clz.getSuperclass()) {
            try {
                method = clz.getDeclaredMethod(methodName, parameterTypes);
            } catch (NoSuchMethodException e) {
            }
        }
        return method;
    }
}
